package com.hqd.ch03.v29.tx.transaction.interceptor;

import com.hqd.ch03.utils.ClassUtils;
import com.hqd.ch03.utils.PatternMatchUtils;
import com.hqd.ch03.v29.aware.EmbeddedValueResolverAware;
import com.hqd.ch03.v29.aware.InitializingBean;
import com.hqd.ch03.v29.config.StringValueResolver;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class NameMatchTransactionAttributeSource implements TransactionAttributeSource, EmbeddedValueResolverAware, InitializingBean {
    private final Map<String, TransactionAttribute> nameMap = new HashMap<>();

    private StringValueResolver embeddedValueResolver;

    public Map<String, TransactionAttribute> getNameMap() {
        return nameMap;
    }

    public void setNameMap(Map<String, TransactionAttribute> nameMap) {
        nameMap.forEach(this::addTransactionalMethod);
    }

    public void addTransactionalMethod(String methodName, TransactionAttribute attr) {
        if (this.embeddedValueResolver != null && attr instanceof DefaultTransactionAttribute) {
            ((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
        }
        this.nameMap.put(methodName, attr);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.embeddedValueResolver = resolver;
    }

    @Override
    public void afterPropertiesSet() {
        for (TransactionAttribute attr : this.nameMap.values()) {
            if (attr instanceof DefaultTransactionAttribute) {
                ((DefaultTransactionAttribute) attr).resolveAttributeStrings(this.embeddedValueResolver);
            }
        }
    }


    @Override
    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        if (!ClassUtils.isUserLevelMethod(method)) {
            return null;
        }

        String methodName = method.getName();
        TransactionAttribute attr = this.nameMap.get(methodName);

        if (attr == null) {
            String bestNameMatch = null;
            for (String mappedName : this.nameMap.keySet()) {
                if (isMatch(methodName, mappedName) &&
                        (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
                    attr = this.nameMap.get(mappedName);
                    bestNameMatch = mappedName;
                }
            }
        }

        return attr;
    }

    protected boolean isMatch(String methodName, String mappedName) {
        return PatternMatchUtils.simpleMatch(mappedName, methodName);
    }
}
